﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ZOVNP.Model.Abstracts
{
    /// <summary> Атрибут для трансляции обёрточных булевых свойств в лямбда-выражениях в родные свойства сущностей. </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public class BooleanMappingAttribute : Attribute
    {
        #region Поля

        /// <summary> Имя родного свойства сущности, использующегося для трансляции. </summary>
        public readonly string PropertyName;
        /// <summary> Значение маски, при котором булево свойство принимает значение True. </summary>
        public readonly object BooleanMask;
        /// <summary> Признак, задающий режим сопоставления битов маски результату. </summary>
        /// <remarks>
        /// Если значение этого свойства <c>true</c>, то булев результат равен true, если хотя бы один
        /// из битов маски не 0. В противном случае результат равен true, когда все биты маски равны 1.
        /// </remarks>
        public readonly bool IsFlagsMask;

        #endregion

        #region Конструкторы

        /// <summary> Создать трансляционный атрибут для булева свойства. </summary>
        /// <param name="PropertyName"> Имя родного свойства сущности, использующегося для трансляции. </param>
        /// <param name="BooleanMask"> Значение маски, при котором булево свойство принимает значение True. </param>
        /// <param name="IsFlagsMask"> Признак, задающий режим сопоставления битов маски результату. </param>
        public BooleanMappingAttribute(string PropertyName, object BooleanMask, bool IsFlagsMask = false)
        {
            if (BooleanMask == null) throw new ArgumentNullException("BooleanMask", "Не задана маска.");
            var type = BooleanMask.GetType();
            if (!(type == typeof(Int64) || type == typeof(UInt64) || type == typeof(SByte) || type == typeof(Byte) || type == typeof(Int16) || type == typeof(UInt16) || type == typeof(Int32) || type == typeof(UInt32)))
                throw new ArgumentException(Properties.Resources.MSG_BadMaskType, "BooleanMask");
            if (BooleanMask.Equals(GetZero(type)))
                throw new ArgumentException(Properties.Resources.MSG_ZeroMask, "BooleanMask");
            this.PropertyName = PropertyName;
            this.BooleanMask = BooleanMask;
            this.IsFlagsMask = IsFlagsMask;
        }

        #endregion

        #region Свойства

        /// <summary> Получение нуля. </summary>
        /// <value> Ноль заданного типа. </value>
        public object Zero { get { return GetZero(BooleanMask.GetType()); } }

        #endregion

        #region Методы

        /// <summary> Получение нуля. </summary>
        /// <param name="NumType"> Тип нуля. </param>
        /// <value> Ноль заданного типа. </value>
        internal static object GetZero(Type NumType)
        {
            if (NumType == typeof(Int64)) return (Int64)0;
            if (NumType == typeof(UInt64)) return (UInt64)0;
            if (NumType == typeof(SByte)) return (SByte)0;
            if (NumType == typeof(Byte)) return (Byte)0;
            if (NumType == typeof(Int16)) return (Int16)0;
            if (NumType == typeof(UInt16)) return (UInt16)0;
            if (NumType == typeof(Int32)) return (Int32)0;
            return (UInt32)0;
        }

        /// <summary> Преобразовать к строке. </summary>
        /// <returns> Строковое представление атрибута. </returns>
        public override string ToString()
        {
            return string.Format("Трансляция к свойству {0} типа {1} при значении {2}, {3}", PropertyName,
                BooleanMask.GetType(), BooleanMask, IsFlagsMask ? "когда взведён хотя бы один бит маски" : "полное соответствие");
        }

        #endregion
    }
}
